﻿/******************************************************************************

  Copyright (C), 2001-2011, DCN Co., Ltd.

 ******************************************************************************
  File Name     : my_common.c
  Version       : Initial Draft
  Author        : tongzhilin
  Created       : 2020/10/12
  Last Modified :
  Description   : my_fifo
  Function List :
  History       :
  1.Date        : 2020/10/30
    Author      : tongzhilin
    Modification: Created file

******************************************************************************/
#include "my_fifo.h"
#include <jni.h>
#include <string>
#include <android/log.h>

#define LOG_TAG    "my_fifo"
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

#define BC_FIFO_INIT(lck)      pthread_mutex_init(&(lck), NULL)
#define BC_FIFO_DESTORY(lck)   pthread_mutex_destroy(&(lck))
#define BC_FIFO_LOCK(lck)      pthread_mutex_lock(&(lck))
#define BC_FIFO_UNLOCK(lck)    pthread_mutex_unlock(&(lck))

typedef struct 
{
    int     len;
    char*   buf;
}BC_FIFO_DATA_S;

typedef struct
{
    BC_FIFO_TYPE_E   type;
    long long int    base;
    long long int    top;
    long long int m_llDropNum;
    int     m_iMaxPkgCntTmp;  /*fifo达到的最大包个数*/
    int     max_cnt;
    pthread_mutex_t mutex;
    BC_FIFO_DATA_S  data[0];
}BC_FIFO_NODE_S;

void* MyPullOut(bc_fifo_handle _handle, int* FIFO_OUT _len)
{
    int posi = 0;
    void* buf = NULL;
    BC_FIFO_NODE_S* handle_s = (BC_FIFO_NODE_S*)_handle;
    char* tmp = NULL;
    BC_FIFO_DATA_S *data = NULL;

    if(_handle == NULL)
    {
        LOGI("fifo handle null\n");
        return NULL;
    }

    if(handle_s->top > handle_s->base)
    {
        posi = (handle_s->base++)%handle_s->max_cnt;
        if(handle_s->base == handle_s->top)
        {
            handle_s->base = 0;
            handle_s->top = 0;
        }

        tmp = (char*)handle_s->data+posi*sizeof(BC_FIFO_DATA_S);
        data = (BC_FIFO_DATA_S*)tmp;

        buf =  data->buf;
        if(_len)
            *_len = data->len;
    }

    if(buf == NULL)
    {
        if(_len)
            *_len = 0;
    }

    return buf;
}

//fifo 本身不涉及malloc和free，用户自行malloc和free.but---当fifo循环覆盖时会自己free
bc_fifo_handle fifo_Create(BC_FIFO_TYPE_E _type, int _max_count)
{
    BC_FIFO_NODE_S* handle_s = NULL;

    handle_s = (BC_FIFO_NODE_S*)malloc(sizeof(BC_FIFO_NODE_S) + _max_count*sizeof(BC_FIFO_DATA_S));

    if(handle_s == NULL)
    {
        LOGI("malloc error\n");
        return NULL;
    }

    memset(handle_s, 0, sizeof(BC_FIFO_NODE_S) + _max_count*sizeof(BC_FIFO_DATA_S));

    handle_s->type = _type;
    handle_s->max_cnt = _max_count;
    handle_s->m_llDropNum = 0;
    handle_s->m_iMaxPkgCntTmp = 0;
    BC_FIFO_INIT(handle_s->mutex);

    return handle_s;
}

int fifo_Destory(bc_fifo_handle _handle)
{
    struct list_head *list_pos = NULL;
    void* buf = NULL;
    BC_FIFO_NODE_S* handle_s = (BC_FIFO_NODE_S*)_handle;
    int count = 0;

    if(_handle == NULL)
    {
        LOGI("fifo handle null\n");
        return BC_FIFO_HANDLE_NULL;
    }
    BC_FIFO_LOCK(handle_s->mutex);

    //读出fifo中的数据并free
    while(1)
    {
        buf = NULL;
        buf = MyPullOut(_handle, NULL);
        if(buf == NULL)
        {
            LOGI("fifo destory,fifo addr=%p,remain buf=%d have all free \n", handle_s, count);
            break;
        }

        free(buf);
        count++;
        usleep(10*1000);
    }
    
    BC_FIFO_UNLOCK(handle_s->mutex);

    BC_FIFO_DESTORY(handle_s->mutex);
    free(handle_s);
    LOGI("fifo destory ok! \n");
    return 0;
}

int fifo_Pushin(bc_fifo_handle _handle, void* _data, int _len, void** _data_drop)
{
    int ret = 0;
    int posi = 0;
    int count = 0;
    void* buf = _data;
    BC_FIFO_NODE_S* handle_s = (BC_FIFO_NODE_S*)_handle;
    char* tmp = NULL;
    BC_FIFO_DATA_S* data = NULL;

    if(_handle == NULL)
    {
        LOGI("fifo handle null\n");
        return BC_FIFO_HANDLE_NULL;
    }

    BC_FIFO_LOCK(handle_s->mutex);

    count = handle_s->top-handle_s->base;
    if(count > handle_s->m_iMaxPkgCntTmp)
        handle_s->m_iMaxPkgCntTmp = count+1;

    if(buf == NULL)
    {
        LOGI("fifo try pushin null data\n");
        ret = BC_FIFO_PUSH_IN_NULL;
        goto End;
    }

    if(count < handle_s->max_cnt)
    {
        posi = (handle_s->top++)%handle_s->max_cnt;
        tmp = (char*)handle_s->data+posi*sizeof(BC_FIFO_DATA_S);
        //先取出地址再做类型转换，稳妥
        data = (BC_FIFO_DATA_S*)tmp;

        data->len = _len;
        data->buf = (char *)buf;
    }
    else
    {
        if(handle_s->type == FIFO_DRP || handle_s->type == FIFO_DRP_AUTO)
        {
            //LOGI("fifo full and drop new data,%s", __func__);
            ret = BC_FIFO_ERR_DROP_NEW_DATA;
            goto End;
        }
        else if(handle_s->type == FIFO_SEQ)
        {
            //等待
            do
            {
                BC_FIFO_UNLOCK(handle_s->mutex);
                usleep(10*1000);
                BC_FIFO_LOCK(handle_s->mutex);
                count = handle_s->top-handle_s->base;
            }while(count >= handle_s->max_cnt);

            posi = (handle_s->top++)%handle_s->max_cnt;
            tmp = (char*)handle_s->data+posi*sizeof(BC_FIFO_DATA_S);
                data = (BC_FIFO_DATA_S*)tmp;

            data->len = _len;
            data->buf = (char *)buf;
        }
        else if(handle_s->type == FIFO_CYC || handle_s->type == FIFO_CYC_AUTO)
        {
            //删除base层数据
            posi = (handle_s->top++)%handle_s->max_cnt;
            tmp = (char*)handle_s->data+posi*sizeof(BC_FIFO_DATA_S);
                data = (BC_FIFO_DATA_S*)tmp;

            data->len = 0;

            handle_s->m_llDropNum++;

            //自动回收内存
            if(handle_s->type == FIFO_CYC_AUTO)
            {
                if(handle_s->m_llDropNum % 100 == 0)
                    LOGI("free buf:%p,fifo=%p\n", data->buf,_handle);
                free(data->buf);
            }
            else
            {
                if(handle_s->m_llDropNum % 100 == 0)
                    LOGI("please drop buf out:%p,fifo=%p\n", data->buf,_handle);
                *_data_drop = data->buf;
            }

            //在此位置上push
            data->len = _len;
            data->buf = (char *)buf;
            //base+1
            handle_s->base++;
            
            //虽然失败了。但是不End，需要更新循环删除次数
            ret = BC_FIFO_ERR_DROP_OLD_DATA;
        }
        else
        {
            LOGI("FIFO type error!\n");
        }
    }
End:
    BC_FIFO_UNLOCK(handle_s->mutex);
    return ret;
}

void* fifo_Pullout(bc_fifo_handle _handle, int* FIFO_OUT _len)
{
    void* buf = NULL;
    BC_FIFO_NODE_S* handle_s = (BC_FIFO_NODE_S*)_handle;

    if(_handle == NULL)
    {
        LOGI("fifo handle null\n");
        return NULL;
    }

    BC_FIFO_LOCK(handle_s->mutex);

    buf = MyPullOut(_handle, _len);

    BC_FIFO_UNLOCK(handle_s->mutex);

    return buf;
}

int fifo_GetNum(bc_fifo_handle _handle)
{
    int iNum = 0;
    BC_FIFO_NODE_S* handle_s = (BC_FIFO_NODE_S*)_handle;

    if(_handle == NULL)
    {
        LOGI("fifo handle null");
        iNum = -1;
        goto End;
    }
    BC_FIFO_LOCK(handle_s->mutex);

    iNum = handle_s->top - handle_s->base;

    BC_FIFO_UNLOCK(handle_s->mutex);
End:
    return iNum; 
}


